﻿//Copyright (c) Microsoft Corporation
//
//All rights reserved. 
//
//Licensed under the Apache License, Version 2.0 (the ""License""); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.

using FluentAssertions;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using TED.WindowsAzure.Unplugged.Mobile.RemoteStorage.AzureBlob.Storage;
using TED.WindowsAzure.Unplugged.Mobile.Tests.Helpers;

namespace TED.WindowsAzure.Unplugged.Mobile.Tests.IntegrationTests
{
    [TestClass]
    public class PerfTests : IntegrationTestBase
    {

        private static string _nativeFolderName;

        private static string _unpluggedFolderName;
        private CloudBlobHelper _nativeFolderPush;
        private CloudBlobHelper _unpluggedFolderPush;
 
        private float allowedOverhead = 1.3F;

        [TestInitialize]
        public void Setup()
        {

        _nativeFolderName = string.Format( "test-{0}",
                            DateTime.Now.Ticks );
        _unpluggedFolderName = string.Format( "test-{0}",
                                    DateTime.Now.Ticks );
        _nativeFolderPush = new CloudBlobHelper( _nativeFolderName );
        _unpluggedFolderPush = new CloudBlobHelper( _unpluggedFolderName );
        }

        [TestCleanup]
        public async Task TearDown()
        {

            // Clean up local FS
            await BinaryStorageHelper.CleanUp();
            // Only clean up the files pushed to the server
            await _nativeFolderPush.CleanUpBlob();
            await _unpluggedFolderPush.CleanUpBlob();
        }

        [TestMethod]
        public async Task Push_1_1K_Files()
        {
            // Arrange
            int size = 175;
            int numfiles = 1;
            
            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size);
            
            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );
            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Push_1_1M_Files()
        {
            // Arrange
            int size = 175000;
            int numfiles = 1;

            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size );

            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );
            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Push_1_10M_Files()
        {
            // Arrange
            int size = 1750000;
            int numfiles = 1;

            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size );
            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );

            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Push_10_1K_Files()
        {
            // Arrange
            int size = 175;
            int numfiles = 10;

            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size );

            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );
            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Push_10_1M_Files()
        {
            // Arrange
            int size = 175000;
            int numfiles = 10;

            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size );

            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );
            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Push_10_10M_Files()
        {
            // Arrange
            int size = 1750000;
            int numfiles = 10;

            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size );
            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );

            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Push_100_1K_Files()
        {
            // Arrange
            int size = 175;
            int numfiles = 100;

            TimeSpan basetime = await nativePush( _nativeFolderPush, numfiles, size );

            TimeSpan timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );
            // Act
            TimeSpan elapsedTime = await unpluggedPush( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            // Assert
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Pull_100_1K_Files()
        {
            int numfiles = 100;
            int size = 175;

            //Arrange
            await _unpluggedFolderPush.FillBlobStorage( numfiles, size );

            var basetime = await nativePull( _unpluggedFolderPush, numfiles, size );
            var timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );
   
            TimeSpan elapsedTime = await unpluggedPull( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Pull_10_1M_Files()
        {
            int numfiles = 10;
            int size = 175000;

            //Arrange
            await _unpluggedFolderPush.FillBlobStorage( numfiles, size );

            var basetime = await nativePull( _unpluggedFolderPush, numfiles, size );
            var timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );

            TimeSpan elapsedTime = await unpluggedPull( numfiles, size );

            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]
        public async Task Pull_10_10M_Files()
        {
            int numfiles = 10;
            int size = 1750000;

            //Arrange
            await _unpluggedFolderPush.FillBlobStorage( numfiles, size );

            var basetime = await nativePull( _unpluggedFolderPush, numfiles, size );
            var timelimit = new TimeSpan( 0, 0, 0, 0, (int)(basetime.TotalMilliseconds * allowedOverhead) );

            TimeSpan elapsedTime = await unpluggedPull( numfiles, size );
            Debug.WriteLine( basetime + " : " + elapsedTime );
            await AssertLocalFiles( numfiles, _unpluggedFolderName );
            elapsedTime.Should().BeLessOrEqualTo( timelimit,
                "because the elapsed time exceeds the time to natively download blob files by" + allowedOverhead + "x" );
        }

        [TestMethod]

        private async Task AssertLocalFiles( int num, string foldername )
        {
            Assert.AreEqual( num, TableHelper.GetCount() );

            Assert.AreEqual( 1, await BinaryStorageHelper.GetCountAsync() );
            Assert.AreEqual( num, await BinaryStorageHelper.GetCountAsync( foldername ) );
        }

        private async Task<TimeSpan> nativePush( CloudBlobHelper helper, int numfiles, int size )
        {
            var startTimeNative = System.DateTime.UtcNow;
            await helper.FillBlobStorage( numfiles, size );
            var endTimeNative = System.DateTime.UtcNow;
            return (endTimeNative - startTimeNative);
        }

        private async Task<TimeSpan> unpluggedPush( int numfiles, int size )
        {

            var startTime = System.DateTime.UtcNow;
            var client = new UnpluggedMobileServiceClient( this.MobileServicesUri, this.MobileServicesKey );

            await client.SyncContext.InitializeAsync( new WindowsAzureBlobStorage( client ) );

            var folder = await
                client.SyncContext.GetSyncFolderAsync( _unpluggedFolderName );
            // Write 100 1K files
            for (int i = 0; i < numfiles; i++)
            {
                var file = await folder.CreateFileAsync( i + ".txt" );
                using (var s = new StreamWriter( (await file.OpenWriteAsync()).AsStreamForWrite() ))
                {
                    s.Write( Ipsum.GetPhrase( size ) );
                }
                await file.CommitAsync();
            }

            await folder.PushAsync();
            var endTime = System.DateTime.UtcNow;
            return (endTime - startTime);
        }

        private async Task<TimeSpan> nativePull( CloudBlobHelper helper, int numfiles, int size )
        {
            await helper.FillBlobStorage( numfiles, size );

            return await _unpluggedFolderPush.DownloadAllBlobs();
        }

        private async Task<TimeSpan> unpluggedPull( int numfiles, int size )
        {
            var startTime = System.DateTime.UtcNow;
            var client = new UnpluggedMobileServiceClient( this.MobileServicesUri, this.MobileServicesKey );

            await client.SyncContext.InitializeAsync( new WindowsAzureBlobStorage( client ) );

            var folder = await
                client.SyncContext.GetSyncFolderAsync( _unpluggedFolderName );

            await folder.PullAsync();
            var endTime = System.DateTime.UtcNow;
            return (endTime - startTime);
        }
    }
}
